#pragma once

#include <unordered_map>
#include <pthread.h>
#include "util.hpp"
#include "config.hpp"

namespace cloud
{
    struct BackupInfo
    {
        bool pack_flag;
        size_t fsize;
        time_t mtime;
        time_t atime;
        string real_path;
        string pack_path;
        string url;
        bool NewBackupInfo(const string &realpath)
        {
            Config *config = Config::GetInstance();
            string packdir = config->GetPackDir();
            string packsuffix = config->GetPackFileSuffix();
            string download_prefix = config->GetDownloadPrefix();
            Util::FileUtil fu(realpath);
            pack_flag = false;
            fsize = fu.FileSize();
            mtime = fu.LastMTime();
            atime = fu.LastAtime();
            real_path = realpath;

            // 压缩包路径+压缩文件名
            pack_path = packdir + fu.FileName() + packsuffix;

            // 下载目录+文件名
            url = download_prefix + fu.FileName();
            return true;
        }
    };

    class DataManager
    {
    private:
        string _backup_file;
        pthread_rwlock_t _rwlock;
        unordered_map<string, BackupInfo> _table;

    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            pthread_rwlock_init(&_rwlock, nullptr);
            InitLoad();
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }
        bool Insert(const BackupInfo &info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();
        }
        bool Update(const BackupInfo &info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();
        }
        bool GetOneByURL(const string &url, BackupInfo *info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.find(url);
            if (it == _table.end())
            {
                pthread_rwlock_unlock(&_rwlock);
                return false;
            }
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        bool GetOneByRealPath(const string &realpath, BackupInfo *info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            while (it != _table.end())
            {
                if (it->second.real_path == realpath)
                {
                    *info = it->second;
                    pthread_rwlock_unlock(&_rwlock);
                    return true;
                }
                it++;
            }
            pthread_rwlock_unlock(&_rwlock);
            return false;
        }
        bool GetAll(vector<BackupInfo> *arr)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            while (it != _table.end())
            {
                arr->push_back(it->second);
                it++;
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        // 持久化存储
        bool Storage()
        {
            vector<BackupInfo> arr;
            GetAll(&arr);
            Json::Value root;
            for (int i = 0; i < arr.size(); ++i)
            {
                Json::Value item;
                item["pack_flag"] = arr[i].pack_flag;
                item["fsize"] = (unsigned int)arr[i].fsize;
                item["atime"] = (unsigned int)arr[i].atime;
                item["mtime"] = (unsigned int)arr[i].mtime;
                item["real_path"] = arr[i].real_path;
                item["pack_path"] = arr[i].pack_path;
                item["url"] = arr[i].url;
                root.append(item);
            }
            string body;
            Util::JsonUtil::Serialize(root, &body);
            Util::FileUtil fu(_backup_file);
            fu.SetContent(body);
            return true;
        }
        bool InitLoad()
        {
            Util::FileUtil fu(_backup_file);
            if (fu.Exists() == false)
                return true;
            string body;
            fu.GetContent(&body);

            Json::Value root;
            Util::JsonUtil::UnSerialize(body,&root);

            for(int i=0;i<root.size();++i)
            {
                BackupInfo info;
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.fsize = root[i]["fsize"].asInt64();
                info.atime = root[i]["atime"].asInt64();
                info.mtime = root[i]["mtime"].asInt64();
                info.pack_path = root[i]["pack_path"].asString();
                info.real_path = root[i]["real_path"].asString();
                info.url = root[i]["url"].asString();
                Insert(info);
            }
            return true;
        }
    };
}